#ifndef OCK_MEMORY_BRIDGE_OCK_LOGGER_H
#define OCK_MEMORY_BRIDGE_OCK_LOGGER_H
#include <string.h>
#include <sstream>
#include <thread>
#include <mutex>
#include <atomic>
#include <stdexcept>
#include "ock/log/OckHmmLogHandler.h"

namespace ock {
class DftOckHmmLogHandler : OckHmmLogHandler
{
public:
    explicit DftOckHmmLogHandler() = default;
    ~DftOckHmmLogHandler() noexcept = default;
    virtual void Write(int32_t level, const char *levelStr, const char *fileName, uint64_t lineNo, const char *msg) override;
    virtual void Flush(void) override;
};

class NullOckHmmLogHandler : OckHmmLogHandler
{
public:
    explicit NullOckHmmLogHandler() = default;
    ~NullOckHmmLogHandler() noexcept = default;
    virtual void Write(int32_t level, const char *levelStr, const char *fileName, uint64_t lineNo, const char *msg) override;
    virtual void Flush(void) override;
};

class OckLogger
{
public:
    virtual ~OckLogger() noexcept = default;
    explicit OckLogger(void);
    int32_t GetStartLevel(void) const;
    void Write(int32_t level, const char *levelStr, const char *fileName, uint64_t lineNo, const char *msg);
    void SetHandler(std::shared_ptr<OckHmmLogHandler> handler);
    void SetLevel(int32_t level);
    static OckLogger &Instance(void);
private:
    std::shared_ptr<OckHmmLogHandler> handler;
    std::atomic<int32_t> startLevel;
    std::mutex handlerMutex;
};

#define OCK_HMM_LOG_FILENAME(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

#define INNER_OCK_LOG(level, levelStr, msg, moduleName)                                                               \
    do {                                                                                                              \
        if (level >= OckLogger::Instance().GetStartLevel()) {                                                         \
            std::ostringstream tmpLogOsStr;                                                                           \
            tmpLogOsStr << "[" << moduleName << "]" << msg;                                                           \
            OckLogger::Instance().Write(level, levelStr, OCK_HMM_LOG_FILENAME, __LINE__, tmpLogOsStr.str().c_str());  \
        }                                                                                                             \
    } while (0);

#define INNER_OCK_LOG(exceptClass, msg, moduleName)                                               \              
    do {                                                                                          \
        if (OCK_LOG_LEVEL_FATAL >= OckLogger::Instance().GetStartLevel()) {                       \
            std::ostringstream tmpLogOsStr;                                                       \
            tmpLogOsStr << "[" << moduleName << "]" << msg;                                       \
            OckLogger::Instance().Write(                                                          \
                OCK_LOG_LEVEL_FATAL, OCK_HMM_LOG_FILENAME, __LINE__, tmpLogOsStr.str().c_str());  \
            throw exceptClass(tmpLogOsStr.str());                                                 \
        }                                                                                         \
    } while (0);
    
#define OCK_LOG_LEVEL_DEBUG(msg) INNER_OCK_LOG(OCK_LOG_LEVEL_DEBUG, "DEBUG", msg, "HMM")
#define OCK_LOG_LEVEL_INFO(msg) INNER_OCK_LOG(OCK_LOG_LEVEL_INFO, "INFO", msg, "HMM")
#define OCK_LOG_LEVEL_WARN(msg) INNER_OCK_LOG(OCK_LOG_LEVEL_WARN, "WARN", msg, "HMM")
#define OCK_LOG_LEVEL_ERROR(msg) INNER_OCK_LOG(OCK_LOG_LEVEL_ERROR, "ERROR", msg, "HMM")
#define OCK_LOG_LEVEL_FATAL(msg) INNER_OCK_LOG(OCK_LOG_LEVEL_FATAL, "FATAL", msg, "HMM")
#define OCK_LOG_LEVEL_THROW(exceptClass, msg) INNER_OCK_THROW(exceptClass, msg, "HMM")

}
#endif